package main

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/jhillyerd/enmime"
)

type EmailMessage struct {
	Id          int       `json:"id"`
	From        string    `json:"from"`
	To          string    `json:"to"`
	Date        time.Time `json:"date"`
	Subject     string    `json:"subject"`
	TextBody    string    `json:"text_body"`
	HTMLBody    string    `json:"html_body"`
	Inlines     int       `json:"inlines"`
	Attachments int       `json:"attachments"`
}

type InboxService struct {
	User      string
	Password  string
	proxyAddr string
	pop3      *Pop3
	pop3Conn  *Pop3Conn
	messages  map[int]*EmailMessage
	loadSize  int
}

func NewInboxService(user, password string) *InboxService {
	return &InboxService{
		User:      user,
		Password:  password,
		proxyAddr: getProxy(),
		messages:  make(map[int]*EmailMessage),
		loadSize:  100,
	}
}

func (s *InboxService) Connect() error {
	var err error

	// SOCKS5 代理地址
	pop3Server := GetEmailPop3Server(s.User)
	if pop3Server == nil {
		return fmt.Errorf("不支持 %s 邮箱", s.User)
	}

	// POP3S（TLS）服务器地址，通常使用 995 端口
	pop3Addr := fmt.Sprintf("%s:%d", pop3Server.addr, pop3Server.port)
	s.pop3Conn = NewPop3Conn(pop3Addr, pop3Server.useTLS, s.proxyAddr)
	conn, err := s.pop3Conn.Connect()
	if err != nil {
		return err
	}

	s.pop3 = NewPop3(conn, s.User, s.Password)
	err = s.pop3.Login()
	if err != nil {
		return err
	}
	return nil
}

func (s *InboxService) MessageList() ([]*EmailMessage, error) {
	// 获取邮件列表ids
	ids, err := s.pop3.ListIds()
	if err != nil {
		return nil, err
	}

	// 记录已加载数量
	loadSize := 0

	for _, id := range ids {
		// 如果已经读取则跳过
		if _, ok := s.messages[id]; ok {
			continue
		}

		// 加载数量限制
		loadSize += 1
		if loadSize > s.loadSize {
			break
		}

		// 读取邮件内容
		content, err := s.pop3.ReadContent(id)
		if err != nil {
			return nil, err
		}

		// 解析邮件
		r := strings.NewReader(content)
		env, err := enmime.ReadEnvelope(r)
		if err != nil {
			return nil, err
		}

		// 解析邮件时间
		emailDate, err := ParseEmailDate(env.GetHeader("Date"))
		if err != nil {
			return nil, err
		}

		emailMessage := &EmailMessage{
			Id:          id,
			From:        env.GetHeader("From"),
			To:          env.GetHeader("To"),
			Date:        emailDate,
			Subject:     env.GetHeader("Subject"),
			TextBody:    env.Text,
			HTMLBody:    env.HTML,
			Inlines:     len(env.Inlines),
			Attachments: len(env.Attachments),
		}
		s.messages[id] = emailMessage
	}

	return s.ToMessageArray(), nil
}

func (s *InboxService) ToMessageArray() []*EmailMessage {
	messages := make([]*EmailMessage, 0)
	for _, msg := range s.messages {
		messages = append(messages, msg)
	}
	return messages
}

func (s *InboxService) Close() {
	if s.pop3Conn != nil {
		s.pop3Conn.Disconnect()
	}
}

type Inbox struct {
	inboxService *InboxService
	lastActive   time.Time
}

type InboxPoolService struct {
	inboxs             map[string]*Inbox
	stayActiveDuration time.Duration
	mu                 sync.Mutex
	ctx                context.Context
	cancel             context.CancelFunc
	wg                 sync.WaitGroup
}

func NewInboxPoolService(ctx context.Context) *InboxPoolService {
	inboxPool := &InboxPoolService{
		inboxs:             make(map[string]*Inbox),
		stayActiveDuration: 60 * time.Minute,
	}
	// 使用调用方传入的 ctx，便于外部控制生命周期；若 ctx 为 nil，则退回到 background
	if ctx == nil {
		ctx = context.Background()
	}
	inboxPool.ctx, inboxPool.cancel = context.WithCancel(ctx)

	// 清理长时间未使用的收件箱
	inboxPool.wg.Add(1)
	go func() {
		defer inboxPool.wg.Done()
		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				inboxPool.RemoveInactive()
			case <-inboxPool.ctx.Done():
				return
			}
		}
	}()

	return inboxPool
}

func NewInboxPoolServiceWithBackground() *InboxPoolService {
	return NewInboxPoolService(context.Background())
}

func (p *InboxPoolService) Close() {
	// 停止后台清理 goroutine
	if p.cancel != nil {
		p.cancel()
	}
	p.wg.Wait()

	p.mu.Lock()
	defer p.mu.Unlock()
	for _, inbox := range p.inboxs {
		inbox.inboxService.Close()
	}
	// 清空 map
	p.inboxs = make(map[string]*Inbox)
}

func (p *InboxPoolService) SetStayActiveDuration(duration time.Duration) *InboxPoolService {
	p.stayActiveDuration = duration
	return p
}

func (p *InboxPoolService) GetInbox(user, password string) *Inbox {
	p.mu.Lock()
	defer p.mu.Unlock()

	key := fmt.Sprintf("%s:%s", user, password)
	if inbox, ok := p.inboxs[key]; ok {
		inbox.lastActive = time.Now()
		p.inboxs[key] = inbox
		return inbox
	}
	inboxService := NewInboxService(user, password)
	p.inboxs[key] = &Inbox{
		inboxService: inboxService,
		lastActive:   time.Now(),
	}
	return p.inboxs[key]
}

func (p *InboxPoolService) RemoveInactive() {
	p.mu.Lock()
	defer p.mu.Unlock()

	for key, inbox := range p.inboxs {
		if time.Since(inbox.lastActive) > p.stayActiveDuration {
			inbox.inboxService.Close()
			delete(p.inboxs, key)
		}
	}
}
